﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using CloudSoft.Extensions;

namespace ERPStore.Extensions
{
	public static partial class ModelsExtensions
	{
		#region Product

		public static string EncodedCode(this Models.Product product)
		{
			if (product == null)
			{
				return null;
			}
			var code = product.Code.ToLower().Trim();
			code = code.Replace("/", "__");
			return code;
		}

		/// <summary>
		/// Permet de recuperer la liste des propriétés d'un groupe de propriété pour un proudit donné
		/// </summary>
		/// <param name="product">The product.</param>
		/// <param name="groupName">Name of the group.</param>
		/// <returns></returns>
		public static IEnumerable<string> GetPropertyNameList(this Models.Product product, string groupName)
		{
			return product.ExtendedProperties[groupName].Select(i => i.Key);
		}

		/// <summary>
		/// Permet de recuperer la liste des noms des groupes de propriété
		/// </summary>
		/// <param name="product">The product.</param>
		/// <returns></returns>
		public static IEnumerable<Models.PropertyGroup> GetPropertyGroupNameList(this Models.Product product)
		{
			return product.ExtendedProperties.Select(i => i.Key);
		}

		public static IList<Models.Brand> GetBrandList(this IEnumerable<Models.Product> productList)
		{
			if (productList.IsNullOrEmpty())
			{
				return new List<Models.Brand>();
			}
			return productList.Where(i => i.Brand != null).Select(i => i.Brand).Distinct().OrderBy(i => i.Name).ToList();
		}

		public static int Position(this ERPStore.Models.ProductList list, ERPStore.Models.Product product)
		{
			if (list.IsNullOrEmpty()
				|| product == null)
			{
				return 0;
			}
			return list.IndexOf(product) + 1 + ((list.PageIndex - 1) * list.PageSize);
		}

		/// <summary>
		/// Retourne le prix d'un produit avec l'eco taxe 
		/// </summary>
		/// <param name="product"></param>
		/// <returns></returns>
		public static Models.Price GrandPriceTotal(this Models.Product product)
		{
			var value = product.BestPrice.Value + product.RecyclePrice.Value;
			return new Models.Price(value, product.BestPrice.TaxRate);
		}

		#endregion

		#region ProductCategory

		public static Models.Media GetFirstMedia(this Models.ProductCategory category)
		{
			if (category == null)
			{
				return null;
			}
			if (category.DefaultImage != null)
			{
				return category.DefaultImage;
			}
			if (category.Parent != null)
			{
				return category.Parent.GetFirstMedia();
			}
			return null;
		}

		public static IList<Models.ProductCategory> GetCategoryList(this IEnumerable<Models.Product> productList)
		{
			if (productList.IsNullOrEmpty())
			{
				return new List<Models.ProductCategory>();
			}
			var result = productList.Where(i => i.Category != null).Select(i => i.Category);
			if (result.IsNullOrEmpty())
			{
				return new List<Models.ProductCategory>();
			}
			return result.Distinct().OrderBy(i => i.Name).ToList();
		}

		/// <summary>
		/// Retourne la catégorie raçine.
		/// </summary>
		/// <param name="category">The category.</param>
		/// <returns></returns>
		public static Models.ProductCategory GetRootProductCategory(this Models.ProductCategory category)
		{
			var result = category;
			if (result != null
				&& result.Parent != null)
			{
				result = GetRootProductCategory(category.Parent);
			}
			return result;
		}

		/// <summary>
		/// Retourne la categorie racine en fonction de l'id d'une categorie
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="categoryId">The category id.</param>
		/// <returns></returns>
		public static Models.ProductCategory GetRootProductCategory(this IEnumerable<Models.ProductCategory> list, int categoryId)
		{
			foreach (var category in list)
			{
				if (category.Id == categoryId)
				{
					return category;
				}
				else if (category.Parent != null)
				{
					var result = list.GetRootProductCategory(category.Parent.Id);
					if (result != null)
					{
						return result;
					}
				}
			}
			return null;
		}

		public static Models.ProductCategory DeepFindParent(this IEnumerable<Models.ProductCategory> list, int parentId)
		// where T : Models.ProductCategory
		{
			foreach (var item in list)
			{
				if (item.Id == parentId)
				{
					return item;
				}
				if (item.Children.Count() > 0)
				{
					var result = item.Children.DeepFindParent(parentId);
					if (result != null)
					{
						return result;
					}
				}
			}
			return null;
		}

		public static IEnumerable<T> DeepSelect<T>(this IEnumerable<Models.ProductCategory> list, Func<Models.ProductCategory, T> selector)
		{
			var result = list.Select(selector).ToList();
			foreach (var item in list)
			{
				if (!item.Children.IsNullOrEmpty())
				{
					var subList = item.Children.DeepSelect(selector);
					result.AddRange(subList);
				}
			}
			return result;
		}

		public static IList<Models.ProductCategory> ToFlatList(this IEnumerable<Models.ProductCategory> list)
		// where T : Models.ProductCategory
		{
			var result = new List<Models.ProductCategory>();

			foreach (var item in list)
			{
				result.Add(item);
				if (item.Children.Count() > 0)
				{
					var flatChildren = item.Children.ToFlatList();
					foreach (var subitem in flatChildren)
					{
						result.Add(subitem);
					}
					item.Children.Clear();
				}
			}

			return result;
		}

		public static Models.ProductCategory DeepFirst(this IEnumerable<Models.ProductCategory> list, Predicate<Models.ProductCategory> predicate)
		// where T : Models.ProductCategory
		{
			foreach (var item in list)
			{
				if (predicate.Invoke(item))
				{
					return item;
				}
				if (item.Children.Count() > 0)
				{
					var result = item.Children.DeepFirst(predicate);
					if (result != null)
					{
						return result;
					}
				}
			}
			return null;
		}

		public static Models.ProductCategory DeepFirstOrDefault(this IEnumerable<Models.ProductCategory> list, Func<Models.ProductCategory, bool> predicate)
		// where T : Models.ProductCategory
		{
			foreach (var item in list)
			{
				if (predicate.Invoke(item))
				{
					return item;
				}
				if (item.Children.Count() > 0)
				{
					var result = item.Children.DeepFirstOrDefault(predicate);
					if (result != null)
					{
						return result;
					}
				}
			}
			return null;
		}

		public static void ActionToRoot(this IEnumerable<Models.ProductCategory> list, Models.ProductCategory leaf, Action<Models.ProductCategory> action)
		// where T : Models.ProductCategory
		{
			action.Invoke(leaf);
			if (leaf.Parent != null)
			{
				list.ActionToRoot(leaf.Parent, action);
			}
		}

		public static void DeepRemoveAll(this IList<Models.ProductCategory> list, Func<Models.ProductCategory, bool> predicate)
		// where T : Models.ProductCategory
		{
			while (true)
			{
				var first = list.DeepFirstOrDefault(predicate);
				if (first == null)
				{
					break;
				}
				if (first.Parent != null)
				{
					first.Parent.Children.Remove(first);
				}
				else
				{
					list.Remove(first);
				}
			}
		}

		public static int DeepProductCount(this IEnumerable<Models.ProductCategory> list)
		// where T : Models.ProductCategory
		{
			var result = 0;
			if (list.IsNotNullOrEmpty())
			{
				foreach (var item in list)
				{
					result += item.ProductCount + DeepProductCount(item.Children);
				}
			}
			return result;
		}

		public static void Traverse(this IEnumerable<Models.ProductCategory> list, Action<Models.ProductCategory> action)
		// where T : Models.ProductCategory
		{
			foreach (var item in list)
			{
				action.Invoke(item);
				if (item.Children.IsNotNullOrEmpty())
				{
					item.Children.Traverse(action);
				}
			}
		}

		public static void Hierarchize(this IList<Models.ProductCategory> list)
		// where T : Models.ProductCategory
		{
			var flatList = list.OrderBy(i => i.Level).ToList();
			list.Clear();
			while (true)
			{
				// Premier de la liste plate
				var first = flatList.FirstOrDefault();

				if (first == null)
				{
					break;
				}

				// Recherche du parent
				if (first.Parent == null)
				{
					list.Add(first);
				}
				else
				{
					var parent = list.DeepFindParent(first.Parent.Id);
					if (parent != null)
					{
						parent.Children.Add(first);
					}
				}

				// suppression on passe au suivant
				flatList.Remove(first);
			}
		}

		public static ERPStore.Models.ProductCategory DeepQuery(this IEnumerable<ERPStore.Models.ProductCategory> list, Func<ERPStore.Models.ProductCategory, bool> predicate)
		{
			foreach (var item in list)
			{
				bool match = predicate.Invoke(item);
				if (match)
				{
					return item;
				}
				if (item.Children.IsNotNullOrEmpty())
				{
					var cat = item.Children.DeepQuery(predicate);
					if (cat != null)
					{
						return cat;
					}
				}
			}
			return null;
		}

		#endregion

		public static decimal GetFeeTotal(this Models.ProductShipping shipping, int quantity, decimal unitPriceByLevel)
		{
			switch (shipping.Strategy)
			{
				case Models.FeeTransportStrategy.FixedPrice:
					return shipping.FixedPrice * quantity;
				case Models.FeeTransportStrategy.ByLevel:
					return shipping.Level * quantity * unitPriceByLevel;
			}
			return 0;
		}


	}
}
